package com.iqiyi.pps.epg.core.service.stream;

import com.alibaba.fastjson.JSONObject;
import com.iqiyi.kiwi.utils.DateHelper;
import com.iqiyi.pps.epg.api.model.web.encode.TEncodeServer;
import com.iqiyi.pps.epg.api.model.web.query.TFilter;
import com.iqiyi.pps.epg.api.model.web.query.TPage;
import com.iqiyi.pps.epg.api.model.web.stream.TChannelStream;
import com.iqiyi.pps.epg.api.model.web.stream.TChannelStreamPage;
import com.iqiyi.pps.epg.core.dao.base.Page;
import com.iqiyi.pps.epg.core.dao.base.PropertyFilter;
import com.iqiyi.pps.epg.core.dao.stream.ChannelStreamBaseDao;
import com.iqiyi.pps.epg.core.model.channel.ChannelBase;
import com.iqiyi.pps.epg.core.model.channel.ChannelConfig;
import com.iqiyi.pps.epg.core.model.rtmp.RtmpServer;
import com.iqiyi.pps.epg.core.model.stream.LiveChannelStream;
import com.iqiyi.pps.epg.core.model.stream.LiveStream;
import com.iqiyi.pps.epg.core.service.channel.ChannelService;
import com.iqiyi.pps.epg.core.service.rtmp.RtmpService;
import com.iqiyi.pps.epg.core.utils.*;
import com.iqiyi.pps.epg.core.utils.model.Tuple2;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.type.IntegerType;
import org.hibernate.type.LongType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.*;

@Service
@Transactional
public class LiveChannelStreamServiceImpl implements LiveChannelStreamService {
    private static final String RTMP_URL_TEMPLATE = "rtmp://%s/live/%s";
    private static Logger logger = LoggerFactory.getLogger(LiveChannelStreamServiceImpl.class);

    @Autowired
    private ChannelStreamBaseDao channelStreamBaseDao;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private RtmpService rtmpService;

    @Autowired
    private LiveStreamService liveStreamService;

    @Override
    public LiveChannelStream get(String streamName) {
        return channelStreamBaseDao.get(streamName);
    }

    @Override
    public LiveChannelStream get(Long fatherId, String streamName) {
        return channelStreamBaseDao.get(fatherId, streamName);
    }

    @Override
    public void save(LiveChannelStream liveChannelStream) {
        channelStreamBaseDao.save(liveChannelStream);
    }

    @Override
    public void delete(LiveChannelStream liveChannelStream) {
        channelStreamBaseDao.delete(liveChannelStream);
    }

    @Override
    public void delete(Long fatherId, String streamName) {
        channelStreamBaseDao.delete(fatherId, streamName);
    }

    @Override
    public List<LiveChannelStream> getByFatherId(Long fatherId) {
        return channelStreamBaseDao.getByFatherId(fatherId);
    }

    @Override
    public LiveChannelStream getById(Long id) {
        return channelStreamBaseDao.getById(id);
    }

    @Override
    public void edit(LiveChannelStream liveChannelStream) {
        channelStreamBaseDao.save(liveChannelStream);
    }

    @Override
    public LiveChannelStream getByStreamType(Long fatherId, String streamType, String nStreamName) {
        return channelStreamBaseDao.getByStreamType(fatherId, streamType, nStreamName);
    }

    @Override
    public LiveChannelStream getByStreamType(Long fatherId, String streamType) {
        return channelStreamBaseDao.getByStreamType(fatherId, streamType);
    }

    @Override
    public LiveChannelStream getByStreamTypeWithIsEnable(Long fatherId, String streamType, boolean isEnable) {
        return channelStreamBaseDao.getByStreamType(fatherId, streamType, isEnable);
    }

    @Override
    public List<LiveChannelStream> getByIsEnable(Long fatherId, boolean isEnable) {
        return channelStreamBaseDao.getByIsEnable(fatherId, isEnable);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public long count(Long fatherId) {
        return channelStreamBaseDao.countLiveChannelStream(fatherId);
    }

    @Override
    public List<LiveChannelStream> getAll() {
        return channelStreamBaseDao.getAll();
    }

    @Override
    public List<LiveChannelStream> getEnableChannelStream() {
        // encodeType = 0 -> H264
        String sql = "SELECT stream.* FROM `live_channel_stream` as stream left join `channel_base` as channel on channel.id = stream.father_id where channel.`is_enable` >= 0 and stream.`status` <> -1 " +
                "and stream.`encode_type` = 0";
        return channelStreamBaseDao.getSession().createSQLQuery(sql)
                .addEntity(LiveChannelStream.class)
                .list();
    }

    @Override
    public List<LiveChannelStream> getByStreamIp(String streamIp) {
        if (StringUtils.isNotEmpty(streamIp)) {
            return channelStreamBaseDao.getByStreamIp(streamIp);
        }
        return null;
    }

    @Override
    public String getByIpAndName(String streamIp, String streamName) {
        LiveChannelStream liveChannelStream = channelStreamBaseDao.get(streamIp,streamName);
        if(liveChannelStream!=null){
            long id = liveChannelStream.getFatherId();
            ChannelBase channelBase = channelService.getChannelBase(id);
            if(channelBase!=null){
                return channelBase.getName();
            }
        }
        return null;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Map<Long, Integer> fetchChannelStream(int liveType) {
        Map<Long, Integer> map = new HashMap<Long, Integer>();
        StringBuffer sql = new StringBuffer();
        sql.append("select stream.father_id channelId, sum( stream.`status` ) totalStatus from channel_base channel , live_channel_stream stream");
        sql.append(" where channel.id = stream.father_id ");
        sql.append(" and channel.is_enable != -1  ");
        sql.append(" and channel.live_type = ? ");
        sql.append(" and stream.`status` != -1 ");
        sql.append(" GROUP BY channelId");
        List<Object[]> result = channelStreamBaseDao.getSession().createSQLQuery(sql.toString())
                .addScalar("channelId", new LongType())
                .addScalar("totalStatus", new IntegerType())
                .setInteger((0), liveType).list();
        for (Object[] simple : result) {
            map.put((Long) simple[0], (Integer) simple[1]);
        }
        return map;
    }

    @Override
    public void updateOnly(LiveChannelStream liveChannelStream) {
        channelStreamBaseDao.updateOnly(liveChannelStream);
    }

    public JSONObject getUrlObj(long fatherId) {
        List<LiveChannelStream> list = this.getByIsEnable(fatherId, true);
        JSONObject jsonObject = new JSONObject();
        if (list.size() == 0) {
            jsonObject.put("output_url", "");
            jsonObject.put("input_url", "");
            return jsonObject;
        }
        LiveChannelStream temp = null;
        if (list.size() == 1) {
            temp = list.get(0);
            String url = String.format(RTMP_URL_TEMPLATE, temp.getStreamIp(), temp.getStreamName());
            jsonObject.put("output_url", url);
            jsonObject.put("input_url", url);
            return jsonObject;
        }

        List<ChannelConfig> configs = channelService.getConfig("StreamType");
        Map<String, Integer> map = new HashMap<String, Integer>();
        for (ChannelConfig channelConfig : configs) {
            map.put(channelConfig.getVal(), channelConfig.getKey());
        }
        Map<Integer, LiveChannelStream> streamMap = new HashMap<>();
        List<Integer> orderList = new ArrayList<Integer>();
        for (LiveChannelStream channelStream : list) {
            streamMap.put(map.get(channelStream.getStreamType()), channelStream);
            orderList.add(map.get(channelStream.getStreamType()));
        }
        Collections.sort(orderList);
        LiveChannelStream tempMin = streamMap.get(orderList.get(0));
        LiveChannelStream tempMax = streamMap.get(orderList.get(orderList.size() - 1));
        jsonObject.put("output_url", String.format(RTMP_URL_TEMPLATE, tempMax.getStreamIp(), tempMax.getStreamName()));
        jsonObject.put("input_url", String.format(RTMP_URL_TEMPLATE, tempMin.getStreamIp(), tempMin.getStreamName()));
        return jsonObject;
    }

    @Override
    public LiveChannelStream getByDolbyStreamName(String dolbyStreamName) {
        if (StringUtils.isNotEmpty(dolbyStreamName)) {
            return channelStreamBaseDao.getByDolbyStreamName(dolbyStreamName);
        }

        return null;
    }

    @Override
    public void stopStream(String streamAddress) {
        List<LiveChannelStream> liveChannelStreamList = channelStreamBaseDao.getByStreamAddress(streamAddress);

        if (liveChannelStreamList.size() == 0) {
            return;
        }

        for (LiveChannelStream stream : liveChannelStreamList) {
            if (stream.getStatus() == LiveChannelStream.STATUS_ACTIVED) {
                stream.setStatus(LiveChannelStream.STATUS_IDLED);
                stream.setBitRate(0L);
                stream.setInputBits("");
                stream.setOutputBits("");
                stream.setAspectRatio("");
                stream.setFrameRate("");
                stream.setScreenSize("");
                stream.setVideo("");
                stream.setAudio("");
                save(stream);
                LiveStreamManager.writeChannelLog(stream.getFatherId(), Constants.LogType.ENCODE_STOP.getIndex(),
                        "编码状态更新为未编码 [streamName=" + stream.getStreamName() + "]");
            }
        }

    }

    @Override
    public void startStream(String streamAddress, TEncodeServer encodeServer) {
        logger.info("[startStream][streamAddress={}][encodeServer={}]", streamAddress, JSONObject.toJSONString(encodeServer));
        List<LiveChannelStream> liveChannelStreamList = channelStreamBaseDao.getByStreamAddress(streamAddress);

        if (liveChannelStreamList.size() == 0) {
            return;
        }

        String extraInfo = encodeServer.getExtraData();
        if (StringUtils.isEmpty(extraInfo)) {
            return;
        }

        extraInfo = extraInfo.replace("\\:", ":");

        JSONObject info = JSONObject.parseObject(extraInfo);

        if (null == info) {
            return;
        }

        for (LiveChannelStream liveChannelStream : liveChannelStreamList) {

//            if(liveChannelStream.getStatus() == LiveChannelStream.STATUS_IDLED) {

            if (info.containsKey("inputBits")) {
                liveChannelStream.setInputBits(info.getString("inputBits"));
            }

            if (info.containsKey("outputBits")) {
                liveChannelStream.setOutputBits(info.getString("outputBits"));
            }

            if (info.containsKey("frameRate")) {
                liveChannelStream.setFrameRate(info.getString("frameRate"));
            }

            if (info.containsKey("bitRate")) {
                liveChannelStream.setBitRate(info.getLong("bitRate"));
            }

            if (info.containsKey("video")) {
                liveChannelStream.setVideo(info.getString("video"));
            }

            if (info.containsKey("audio")) {
                liveChannelStream.setAudio(info.getString("audio"));
            }

            if (info.containsKey("screenSize")) {
                liveChannelStream.setScreenSize(
                        info.getString("screenSize")
                                .replace("X", ":")
                                .replace("x", ":"));
            }

            if (info.containsKey("aspectRatio")) {
                liveChannelStream.setAspectRatio(info.getString("aspectRatio"));
            }

            liveChannelStream.setStatus(LiveChannelStream.STATUS_ACTIVED);
            LiveStreamManager.writeChannelLog(liveChannelStream.getFatherId(), Constants.LogType.ENCODE_START.getIndex(),
                    "编码状态更新为编码中 [streamName=" + liveChannelStream.getStreamName() + "]");

            // sync channel
            ChannelBase channelBase = channelService.getChannelBase(liveChannelStream.getFatherId());
            channelBase.setIsSync(1);
            channelService.saveBase(channelBase);

            save(liveChannelStream);
//            }
        }

    }


    @Override
    public LiveChannelStream getByStreamTypeAndEncodeTypeWithIsEnable(Long fatherId, String streamType, int encodeType, boolean isEnable) {
        return channelStreamBaseDao.getByStreamTypeAndEncodeType(fatherId, streamType, encodeType, isEnable);
    }

    @Override
    public List<LiveChannelStream> getStreamsByStreamType(Long fatherId, String streamType, int encodeType, boolean isEnable) {
        return channelStreamBaseDao.getStreamsByStreamTypeAndEncodeType(fatherId, streamType, encodeType, isEnable);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
    public TChannelStreamPage getSearchList(TPage page, TFilter filter) {
        List<PropertyFilter> filterList = TransUtil.builderFilterList(filter);
        Page<LiveChannelStream> dbPage = TransUtil.buildPage(page);
        Page<LiveChannelStream> pageData =  channelStreamBaseDao.findPage(dbPage, filterList);
        List<LiveChannelStream> dataList = pageData.getResult();
        TChannelStreamPage channelStreamPage = new TChannelStreamPage();
        page.setTotalCount(pageData.getTotalCount());
        channelStreamPage.setTPage(page);
        List<TChannelStream> tChannelStreamList = transToTChannelStreamList(dataList);
        channelStreamPage.setResult(tChannelStreamList);
        return channelStreamPage;
    }

    private List<TChannelStream> transToTChannelStreamList(List<LiveChannelStream> dataList) {
        List<TChannelStream> resultList = new ArrayList<>();
        TChannelStream tChannelStream = null;
        for(LiveChannelStream channelStream : dataList) {
            tChannelStream = transToTChannelStream(channelStream);
            resultList.add(tChannelStream);
        }

        return resultList;
    }

    private TChannelStream transToTChannelStream(LiveChannelStream stream) {
        TChannelStream tChannelStream = new TChannelStream();
        ChannelBase channelBase = channelService.getChannelBase(stream.getFatherId());

        tChannelStream.setId(stream.getId());
        tChannelStream.setFatherId(stream.getFatherId());
        tChannelStream.setChannelName(channelBase.getName());
        tChannelStream.setStreamName(stream.getStreamName());
        tChannelStream.setStreamIp(stream.getStreamIp());
        tChannelStream.setEncodedIp(stream.getEncodedIp());
        tChannelStream.setStatus(stream.getStatus().toString());
        tChannelStream.setInputBits(stream.getInputBits());
        tChannelStream.setOutputBits(stream.getOutputBits());
        tChannelStream.setStreamType(stream.getStreamType());
        tChannelStream.setBitRate(stream.getBitRate());
        tChannelStream.setAspectRatio(stream.getAspectRatio());
        tChannelStream.setScreenSize(stream.getScreenSize());
        tChannelStream.setFrameRate(stream.getFrameRate());
        tChannelStream.setVideo(stream.getVideo());
        tChannelStream.setAudio(stream.getAudio());
        tChannelStream.setPeerCount(stream.getPeerCount());
        tChannelStream.setBcsNum(stream.getBcsNum());
        tChannelStream.setCutterIdFlv(stream.getCutterIdFlv());
        tChannelStream.setCutterIdTs(stream.getCutterIdTs());
        if (stream.getIsEnable()) {
            tChannelStream.setIsEnable("1");
        } else {
            tChannelStream.setIsEnable("0");
        }
        tChannelStream.setAddTime(DateHelper.getDateStringByPattern(stream.getAddTime(), "yyyy-MM-dd HH:mm:ss"));
        tChannelStream.setUpdateTime(DateHelper.getDateStringByPattern(stream.getUpdateTime(), "yyyy-MM-dd HH:mm:ss"));
        tChannelStream.setDest(stream.getDest());
        tChannelStream.setStreamApiVersion(stream.getStreamApiVersion());
        tChannelStream.setDolbyStreamName(stream.getDolbyStreamName());
        tChannelStream.setDolbyStreamUrl(stream.getDolbyStreamUrl());
        tChannelStream.setDolbyAudioChannel(stream.getDolbyAudioChannel());
        tChannelStream.setEncodeType(stream.getEncodeType());
        tChannelStream.setStreamAddress(stream.getStreamAddress());


        RtmpServer rtmpServer = getRtmpServerByPublicIp(tChannelStream.getStreamIp());
        if (null != rtmpServer) {
            tChannelStream.setInternalIp(rtmpServer.getPrivateIp());
//            String outSignal = "rtmp://" + rtmpServer.getPrivateIp() + "/live/" + stream.getStreamName();
//            tChannelStream.setInputSignal(getInputsignalByOutsignal(outSignal));

            tChannelStream.setPort(rtmpServer.getPort());
        } else {
            tChannelStream.setInternalIp("");
//            tChannelStream.setInputSignal("");
            tChannelStream.setPort(RtmpServer.PORT_DEFAULT);
        }

        return tChannelStream;
    }

    private RtmpServer getRtmpServerByPublicIp(String publicIp) {
        if (null == publicIp || "".equals(publicIp)) {
            return null;
        }

        try {
            return rtmpService.getByPublicIp(publicIp);
        } catch (Exception e) {
            logger.info("[getRtmpServerByPublicIp][publicIp={}][failed]", publicIp);
            return null;
        }
    }

    @Transactional
    public void updateChannelStreamIsEnable(LiveChannelStream stream, String isEnable) {
        if(stream.getId() <= 0 || isEnable == null) {
            return;
        }
        String hql = "update LiveChannelStream set isEnable=" + isEnable + " where id=" + stream.getId();
        int executeNum = channelStreamBaseDao.createQuery(hql).executeUpdate();
        logger.info("[updateChannelStreamIsEnable] streamId={},executeNum={}", stream.getId(), executeNum);
    }

    @Transactional
    public void updateChannelStreamStreamUrls(LiveChannelStream stream, String streamUrls, boolean isDolbyStreams) {
        if(stream.getId() <= 0 || null == streamUrls) {
            return;
        }
        String updateColumn = isDolbyStreams ? "dolbyStreamUrls" : "streamUrls";

        try {
//            String hql = "update LiveChannelStream set streamUrls=" + streamUrls.trim() + " where id=" + stream.getId();
            String hql = String.format("update LiveChannelStream set %s='%s' where id=%s", updateColumn, streamUrls.trim(), stream.getId());
            int executeNum = channelStreamBaseDao.createQuery(hql).executeUpdate();
            logger.info("[updateChannelStreamStreamUrls] streamId={},executeNum={},hql={}", stream.getId(), executeNum,hql);
        } catch (Exception e) {
            logger.info("[updateChannelStreamStreamUrls] exception = " + e);
        }
    }

    @Deprecated
    public void updateStreamUrls(LiveChannelStream stream) {
        updateStreamUrlsBackup(stream, true,true);
    }

    /**
     *
     * @param stream
     * @param isMyselfRestart 当前流是否重启,false 过滤当前流
     * @param isOtherRestart  其他流是否重启,false 过滤其他流
     */
    public void updateStreamUrlsBackup(LiveChannelStream stream, boolean isMyselfRestart, boolean isOtherRestart ) {
        if (null == stream) {
            return;
        }

        logger.info("[updateStreamUrls][stream={}]", JSONObject.toJSONString(stream));

        if(isJKStream(stream.getStreamName())) {
            updateChannelStreamStreamUrls(stream, stream.getEncodeType() == LiveChannelStream.ENCODE_H265 ?
                    stream.getStreamAddress() : stream.fetchRtmpUrlUtil(), false);

            if(stream.getStatus() == LiveChannelStream.STATUS_ACTIVED) {
                List<LiveStream> liveStreamList = liveStreamService.get(stream.getFatherId(), stream.getStreamName());
                if (stream.getEncodeType() == LiveChannelStream.ENCODE_H265) {
                    for (LiveStream liveStream : liveStreamList) {
                        if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_TS)) {
                            JSONObject result = LiveStreamManager.restartDolby(liveStream.getDolby_ch(), stream.getStreamAddress(),
                                    stream.getStreamApiVersion(),
                                    stream.getStreamAddress());
                            writeChannelLog(stream.getFatherId(), Constants.LogType.NEWTS_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                    result.toJSONString());
                        }
                    }
                } else if (stream.getEncodeType() == LiveChannelStream.ENCODE_H264) {
                    for (LiveStream liveStream : liveStreamList) {
                        if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_FLV)) {
                            JSONObject result = LiveStreamManager.restartFlv(liveStream.getUrl(), stream.getStreamIp(), stream.getStreamName(),
                                    stream.getStreamApiVersion(), stream.fetchRtmpUrlUtil());
                            writeChannelLog(stream.getFatherId(), Constants.LogType.FLV_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                    result.toJSONString());

                        } else if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_TS)) {
                            JSONObject result = LiveStreamManager.restartTs(liveStream.getUrl(), stream.getStreamIp(), stream.getStreamName(),
                                    stream.getStreamApiVersion(), stream.fetchRtmpUrlUtil());
                            writeChannelLog(stream.getFatherId(), Constants.LogType.TS_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                    result.toJSONString());
                        } else if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_DOLBY)) {
                            if (StringUtils.isNotBlank(stream.getDolbyStreamUrl())) {
                                JSONObject result = LiveStreamManager.restartDolby(liveStream.getDolby_ch(), stream.getDolbyStreamUrl(),
                                        stream.getStreamApiVersion(), stream.getDolbyStreamUrls());
                                writeChannelLog(stream.getFatherId(), Constants.LogType.DOLBY_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                        result.toJSONString());
                            }
                        }
                    }
                }
            }
            return;
        }


        try {
            List<LiveChannelStream> streamList = getStreamsByStreamType(
                    stream.getFatherId(), stream.getStreamType(), stream.getEncodeType(), true);

            if (null == streamList || streamList.size() == 0) {
                return;
            }

            if (stream.getEncodeType() == LiveChannelStream.ENCODE_H265) {
                // h265 只有 newTS
                LinkedList<Tuple2<String,Boolean>> alist = new LinkedList<>();
                for (LiveChannelStream channelStream : streamList) {
                    if(!isJKStream(channelStream.getStreamName())) {
                        alist.add(new Tuple2<>(channelStream.getStreamAddress(), channelStream.getBackupEachOther() == 1));
                    }
                }

                Map<String, String> streamUrlsMap = getStreamUrlsMap(alist);
                for (LiveChannelStream channelStream : streamList) {
                    if(isJKStream(channelStream.getStreamName())) {
                       continue;
                    }

                    String streamUrls = streamUrlsMap.get(channelStream.getStreamAddress());
                    updateChannelStreamStreamUrls(channelStream, streamUrls, false);
                    //自己不重启,同一清晰度的其他流重启
                    if (!isMyselfRestart && channelStream.getStreamName().equals(stream.getStreamName())) {
                        continue;
                    }

                    //自己重启,同一清晰度的其他流不重启
                    if (!isOtherRestart && !channelStream.getStreamName().equals(stream.getStreamName())) {
                        continue;
                    }
                    if(channelStream.getStatus() == LiveChannelStream.STATUS_ACTIVED) {
                        List<LiveStream> liveStreamList = liveStreamService.get(channelStream.getFatherId(), channelStream.getStreamName());
                        for (LiveStream liveStream : liveStreamList) {
                            if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_TS)) {
                                JSONObject result = LiveStreamManager.restartDolby(liveStream.getDolby_ch(), channelStream.getStreamAddress(),
                                        channelStream.getStreamApiVersion(),
                                        streamUrls);
                                writeChannelLog(channelStream.getFatherId(), Constants.LogType.NEWTS_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                        result.toJSONString());
                            }
                        }
                    }
                }

            } else {
                LinkedList<Tuple2<String,Boolean>> alist = new LinkedList<>();
                for (LiveChannelStream channelStream : streamList) {
                    if(isJKStream(channelStream.getStreamName())) {
                        continue;
                    }
                    alist.add(new Tuple2<>(channelStream.fetchRtmpUrlUtil(), channelStream.getBackupEachOther() == 1));
                }

                LinkedList<Tuple2<String,Boolean>> dolbyList = new LinkedList<>();

                Map<String, String> streamUrlsMap = getStreamUrlsMap(alist);
                String updatedStreamUrls = streamUrlsMap.get(stream.fetchRtmpUrlUtil());

                writeChannelLog(stream.getFatherId(), 201, Constants.SYSTEM_USER_SIGN, "更新后的流切片策略 [streamName=" + stream.getStreamName() +"][streamUrls="+updatedStreamUrls+"]");

                for (LiveChannelStream channelStream : streamList) {

                    if(isJKStream(channelStream.getStreamName())) {
                        continue;
                    }

                    String streamUrls = streamUrlsMap.get(channelStream.fetchRtmpUrlUtil());
                    updateChannelStreamStreamUrls(channelStream, streamUrls, false);

                    if(StringUtils.isNotBlank(channelStream.getDolbyStreamUrl())) {
                        dolbyList.add(new Tuple2<>(channelStream.getDolbyStreamUrl(), channelStream.getBackupEachOther() == 1));
                    }
                    //自己不重启,同一清晰度的其他流重启
                    if (!isMyselfRestart && channelStream.getStreamName().equals(stream.getStreamName())) {
                        continue;
                    }

                    //自己重启,同一清晰度的其他流不重启
                    if (!isOtherRestart && !channelStream.getStreamName().equals(stream.getStreamName())) {
                        continue;
                    }

                    if(channelStream.getStatus() == LiveChannelStream.STATUS_ACTIVED) {
                        List<LiveStream> liveStreamList = liveStreamService.get(channelStream.getFatherId(), channelStream.getStreamName());
                        for (LiveStream liveStream : liveStreamList) {
                            if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_FLV)) {
                                JSONObject result = LiveStreamManager.restartFlv(liveStream.getUrl(), channelStream.getStreamIp(), channelStream.getStreamName(),
                                        channelStream.getStreamApiVersion(), streamUrls);
                                writeChannelLog(channelStream.getFatherId(), Constants.LogType.FLV_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                        result.toJSONString());

                            } else if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_TS)) {
                                JSONObject result = LiveStreamManager.restartTs(liveStream.getUrl(), channelStream.getStreamIp(), channelStream.getStreamName(),
                                        channelStream.getStreamApiVersion(), streamUrls);
                                writeChannelLog(channelStream.getFatherId(), Constants.LogType.TS_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                        result.toJSONString());
                            }
                        }
                    }



                }

                if(dolbyList.size() >= 1) {
                    Map<String, String> dolbyStreamUrlsMap = getStreamUrlsMap(dolbyList);
                    for(LiveChannelStream channelStream : streamList) {
                        String dolbyStreamUrls = dolbyStreamUrlsMap.get(channelStream.getDolbyStreamUrl());
                        if (StringUtils.isBlank(dolbyStreamUrls) || isJKStream(channelStream.getStreamName())) {
                            continue;
                        }

                        updateChannelStreamStreamUrls(channelStream, dolbyStreamUrls, true);

                        //自己不重启,同一清晰度的其他流重启
                        if (!isMyselfRestart && channelStream.getStreamName().equals(stream.getStreamName())) {
                            continue;
                        }

                        //自己重启,同一清晰度的其他流不重启
                        if (!isOtherRestart && !channelStream.getStreamName().equals(stream.getStreamName())) {
                            continue;
                        }
                        if(channelStream.getStatus() == LiveChannelStream.STATUS_ACTIVED) {
                            List<LiveStream> liveStreamList = liveStreamService.get(channelStream.getFatherId(), channelStream.getStreamName());
                            for (LiveStream liveStream : liveStreamList) {
                                if (liveStream.getFormatType().equals(LiveStream.FORMATTYPE_DOLBY)) {
                                    JSONObject result = LiveStreamManager.restartDolby(liveStream.getDolby_ch(), channelStream.getDolbyStreamUrl(),
                                            channelStream.getStreamApiVersion(), dolbyStreamUrls);
                                    writeChannelLog(channelStream.getFatherId(), Constants.LogType.DOLBY_RESTART.getIndex(), Constants.SYSTEM_USER_SIGN,
                                            result.toJSONString());
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.info("[updateStreamUrls] exception = " + e);
        }

    }

    @Deprecated
    private Map<String, String> getStreamUrlsMapDeprecated(LinkedList<String> alist) {
        Map<String, String> result = new HashMap<>();

        int size = alist.size();

        if(size == 1) {
            result.put(alist.get(0), alist.get(0));
        }else {
            for (int i = 0; i < size; i++) {
                result.put(alist.get(i), String.format("%s %s",
                        StringUtils.join(alist.subList(i, size), " "), StringUtils.join(alist.subList(0, i), " ")));
            }
        }

        return result;
    }

    private Map<String, String> getStreamUrlsMap(LinkedList<Tuple2<String,Boolean>> alist) {
        Map<String, String> result = new HashMap<>();

        int size = alist.size();

        if(size == 1) {
            result.put(alist.get(0).t, alist.get(0).t);// t 表示 url, s表示是否灾备
        }else {
            for (int index = 0; index < alist.size(); index++) {
                Tuple2<String, Boolean> tuple2 = alist.get(index);
                String key = tuple2.t;
                StringBuilder urlsBuilder = new StringBuilder();
                //首先加入自身url
                urlsBuilder.append(alist.get(index).t + " ");
                //循环当前之后的流
                for (int i1 = index + 1 ; i1 < alist.size(); i1++) {
                    if (alist.get(i1).s) {
                        urlsBuilder.append(alist.get(i1).t + " ");
                    }

                }
                //循环当前之前的流, 这里需要回到0,否则前面的流取不完全
                if (index - 1 >= 0) {
                    for (int i2 = 0; i2 < index ; i2++) {
                        if (alist.get(i2).s) {
                            urlsBuilder.append(alist.get(i2).t + " ");
                        }

                    }
                }
                result.put(key, urlsBuilder.toString().trim());
            }

        }

        return result;
    }


    private void writeChannelLog(long channelId, int type, String user, String descr) {
        WriteSystemLogUtil.getInstance().writeChannelLog(channelId, type, user, descr);
    }

    private boolean isJKStream(String streamName) {
        return StringUtils.isNotBlank(streamName) && streamName.endsWith("_jk");

    }
}
